#include "entities/improvement.h"

#include <algorithm>

#include "gamestate.h"
#include "luainterface.h"
#include "resourcemanager.h"

#include "entities/army.h"
#include "entities/building.h"
#include "entities/unit.h"

Improvement::Improvement():Entity(){
    index = 0;
    city_owner = -1;
    owner = 0;
    turns_to_complete = 0;
    primary_building_id = -1;
    besieged = false;
}

void Improvement::EndTurn(){
    if(IsPrimaryBuildingComplete()){
        DoBuildingConstruction();
    }
}

void Improvement::Select(){
    Entity::Select();
}

std::string Improvement::GetModelName(){
    if(buildings.size()){
        Building* building = gamestate->GetBuilding(GetPrimaryBuildingID());
        if(building->GetTurnsToComplete() == 0){
            return building->GetModelName();
        }
        else{
            return "underconstruction";
        }
    }
    else{
        return "";
    }
}

void Improvement::SetCityOwner(int id){
    city_owner = id;
    for(int building_id:buildings){
        gamestate->GetBuilding(building_id)->SetCityOwner(id);
    }
}

int Improvement::GetTurnsToComplete(){
    if(buildings.size() == 0){
        return 0;
    }
    return gamestate->GetBuilding(GetPrimaryBuildingID())->GetTurnsToComplete();
}

bool Improvement::IsConstructionPaused(){
    if(buildings.size() == 0){
        return false;
    }
    return gamestate->GetBuilding(GetPrimaryBuildingID())->IsConstructionPaused();
}

void Improvement::SetConstructionPaused(bool pause){
    if(buildings.size()){
        gamestate->GetBuilding(GetPrimaryBuildingID())->SetConstructionPaused(pause);
    }
}

void Improvement::Order(int target){
    Entity* targetEntity = gamestate->GetEntity(target);

    switch(targetEntity->GetType()){
        case ET_TILE:{
            if(unitIDs.size() == 0){
                break;
            }
            std::set<int> selected_units = GetSelectedUnits();

            Army* army = gamestate->CreateArmy();
            army->SetIndex(index);
            army->SetOwner(GetOwner());

            RemoveFromUnits(selected_units);
            army->AddToUnits(selected_units);
            break;
        }
        default:{
            std::cout<<"Unhandled Improvement::Order Target: "<<targetEntity->GetType()<<std::endl;
        }
    }
}

std::set<int> Improvement::GetSelectedUnits(){
    std::set<int> selected_units;
    for(int id : unitIDs){
        if(gamestate->GetUnit(id)->IsSelected()){
            selected_units.insert(id);
        }
    }
    if(selected_units.size() == 0){
        selected_units = unitIDs;
    }
    return selected_units;
}

bool Improvement::CanContainUnits(){
    for(int id : buildings){
        Building* building = gamestate->GetBuilding(id);
        if(building->CanContainUnits()){
            return true;
        }
    }
    return false;
}

float Improvement::GetPopulationGrowthFactor(){
    float growth_factor = 0;
    return growth_factor;
}

void Improvement::AddBuilding(int id){
    std::set<std::string> buildingConOps;
    std::set<std::string> buildingRecruits;
    Building* building = gamestate->GetBuilding(id);

    if(buildings.size() == 0){
        SetPrimaryBuildingID(id);
    }

    buildings.insert(id);
    building->SetIndex(GetIndex());
    building->SetContainingEntity(GetID());
    building->SetCityOwner(city_owner);

    // If atleast one of the buidings can contain units then the improvment can
    canContainUnits += building->CanContainUnits();

    for(std::string conOp : building->GetConstructionOptionNames()){
        AddConstructionOption(conOp);
    }
}

void Improvement::RemoveBuilding(int id){
    Building* building = gamestate->GetBuilding(id);

    buildings.erase(id);

    canContainUnits = 0;
    for(int id : buildings){
        canContainUnits += gamestate->GetBuilding(id)->CanContainUnits();
    }

    for(std::string conOp : building->GetConstructionOptionNames()){
        RemoveConstructionOption(conOp);
    }
}

bool Improvement::IsPrimaryBuildingComplete(){
    Building* primary_building = gamestate->GetBuilding(primary_building_id);
    if(primary_building){
        return true;
    }
    else{
        return false;
    }
}

void Improvement::DoBuildingConstruction(){
    if(building_construction_queue.size() > 0){
        Building* building = gamestate->GetBuilding(*(building_construction_queue.begin()));
        building->DecrementTurnsToComplete();

        if(building->GetTurnsToComplete() == 0){
            AddBuilding(building->GetID());
            building_construction_queue.erase(building->GetID());
        }
    }
}

// Normal way of adding a building, ie when it's built through the game
void Improvement::AddBuildingToConstructionQueue(int id){
    building_construction_queue.insert(building_construction_queue.begin(), id);
}

std::set<std::string> Improvement::GetImprovementOptionNames(){
    std::set<std::string> names;

    for(int id : buildings){
        Building* building = gamestate->GetBuilding(id);
        std::set<std::string> option_names = building->GetImprovementOptionNames();
        names.insert(option_names.begin(), option_names.end());
    }

    return names;
}

std::set<std::string> Improvement::GetConstructionOptionNames(){
    std::set<std::string> names;
    std::set<int> building_ids = GetBuildingIDs();
    std::set<int> cons = GetConstructionQueueIDs();

    auto Add = [&names](int id){
        std::set<std::string> option_names = gamestate->GetBuilding(id)->GetConstructionOptionNames();
        names.insert(option_names.begin(), option_names.end());
    };
    auto Erase = [&names](int id){ names.erase(gamestate->GetBuilding(id)->GetName()); };

    // Get all possibles
    std::for_each( building_ids.begin(), building_ids.end(), Add);
    // Remove the ones already built
    std::for_each( building_ids.begin(), building_ids.end(), Erase);
    // And the ones in the construction queue
    std::for_each( cons.begin(), cons.end(), Erase);

    return names;
}
